package com.cgs.mongo.engine;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 线程管理
 */
public class ThreadManager {

    private static final ConcurrentHashMap<String, ExecutorServiceWrapper> EXECUTOR_HOLDER = new ConcurrentHashMap<>(6);
    private static final GC_THREAD GC_THREAD = new GC_THREAD();

    static {
        GC_THREAD.addResources(EXECUTOR_HOLDER.values());
        Runtime.getRuntime().addShutdownHook(GC_THREAD);
    }

    public static ExecutorService getUserExecutor(String groupName) {
        return EXECUTOR_HOLDER.computeIfAbsent(groupName, k -> createDefaultExecutor(groupName, false));
    }

    public static ExecutorService getDaemonExecutor(String groupName) {
        return EXECUTOR_HOLDER.computeIfAbsent(groupName, k -> createDefaultExecutor(groupName, true));
    }

    public static ExecutorServiceWrapper createDefaultExecutor(String groupName, boolean daemon) {
        return new ExecutorServiceWrapper(new ThreadPoolExecutor(2, 8,
                60L, TimeUnit.SECONDS, new SynchronousQueue<>(), new DefaultThreadFactory(groupName, daemon)));
    }

    public static void registry(String groupName, ExecutorService executor) {
        EXECUTOR_HOLDER.putIfAbsent(groupName, new ExecutorServiceWrapper(executor));
    }

    public static void addGcResources(Collection<? extends AutoCloseable> resources) {
        GC_THREAD.addResources(resources);
    }

    public static void addGcResource(AutoCloseable resource) {
        GC_THREAD.addResource(resource);
    }

    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final String namePrefix;
        private final boolean daemon;

        DefaultThreadFactory(String groupName, boolean daemon) {
            group = new ThreadGroup(groupName);
            namePrefix = "STRING-MONGO-POOL-" +
                    POOL_NUMBER.getAndIncrement() +
                    "-THREAD-";
            this.daemon = daemon;
        }
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                    namePrefix + threadNumber.getAndIncrement(),
                    0);
            t.setDaemon(daemon);
            if (t.getPriority() != Thread.NORM_PRIORITY){
                t.setPriority(Thread.NORM_PRIORITY);
            }
            return t;
        }
    }

    /**
     * Gc线程
     */
    static class GC_THREAD extends Thread {

        private List<AutoCloseable> gcResources = new CopyOnWriteArrayList<>();

        @Override
        public void run() {
            close(gcResources.iterator());
        }

        public void close(Iterator<? extends AutoCloseable> iterator) {
            try {
                while (iterator.hasNext()) {
                    iterator.next().close();
                }
            } catch (Exception e) {
                close(iterator);
            }
        }

        void addResources(Collection<? extends AutoCloseable> resources) {
            this.gcResources.addAll(resources);
        }

        void addResource(AutoCloseable resource) {
            this.gcResources.add(resource);
        }

        public GC_THREAD() {
            super("GC-THREAD");
        }
    }

    static class ExecutorServiceWrapper implements ExecutorService, AutoCloseable {

        private final ExecutorService executorService;

        ExecutorServiceWrapper(final ExecutorService executorService) {
            this.executorService = executorService;
        }

        @Override
        public void close() {
            executorService.shutdown();
        }

        @Override
        public void shutdown() {
            executorService.shutdown();
        }

        @Override
        public List<Runnable> shutdownNow() {
            return executorService.shutdownNow();
        }

        @Override
        public boolean isShutdown() {
            return executorService.isShutdown();
        }

        @Override
        public boolean isTerminated() {
            return executorService.isTerminated();
        }

        @Override
        public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException {
            return executorService.awaitTermination(timeout, unit);
        }

        @Override
        public <T> Future<T> submit(final Callable<T> task) {
            return executorService.submit(task);
        }

        @Override
        public <T> Future<T> submit(final Runnable task, final T result) {
            return executorService.submit(task, result);
        }

        @Override
        public Future<?> submit(final Runnable task) {
            return executorService.submit(task);
        }

        @Override
        public <T> List<Future<T>> invokeAll(final Collection<? extends Callable<T>> tasks) throws InterruptedException {
            return executorService.invokeAll(tasks);
        }

        @Override
        public <T> List<Future<T>> invokeAll(final Collection<? extends Callable<T>> tasks, final long timeout, final TimeUnit unit) throws InterruptedException {
            return executorService.invokeAll(tasks, timeout, unit);
        }

        @Override
        public <T> T invokeAny(final Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
            return executorService.invokeAny(tasks);
        }

        @Override
        public <T> T invokeAny(final Collection<? extends Callable<T>> tasks, final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
            return executorService.invokeAny(tasks, timeout, unit);
        }

        @Override
        public void execute(final Runnable command) {
            executorService.execute(command);
        }
    }
}
